#!/bin/bash

# 详解：https://blog.csdn.net/u012581020/article/details/131435946

set -e

# 分成三个块
# awk 'BEGIN{ commands } pattern{ commands } END{ commands }'

# 使用时注意（来自 linux命令及shell.pdf）：
# 1. 确保整个awk 命令用单引号括起来
# 2. 确保命令内所有引号成对出现 
# 3. 确保用 {} 括起动作语句，用() 括起条件语句

source common.sh

# 分组，根据条件求count，也就可以求一列数字小于n的个数
t2_use_for_loop_to_construct_implicit_hashtable() {
    # sum hash table自动创建，初使为0
    awk 'BEGIN {print "Name   Grade"} 
        {sum[$2]+=1} 
        END {for(key in sum) print key "\t" sum[key]}' testdata/test.txt  # ""是用来拼接的

    awk 'BEGIN {print "\npercentile\tcount"} 
        {if($7 < 30) hist[30]+=1
         if($7 < 50) hist[50]+=1
        } 
        END {for(key in hist) print key "\t\t" hist[key]}' testdata/grade.txt
    # 如果阈值只有一种，可以不用table了，直接将字段与此阈值进行比较
    awk 'BEGIN {print "\ncount" }
        {if ($7>40) cnt+=1}
        END {print cnt}' \
        testdata/grade.txt
}

t3_reglar_expr() {
    # 用 if 就要用 ()
    echo_blue "fuzzy match"
    awk '{if($4 ~ /Brown/) print $0}' testdata/grade.txt

    echo_red "not need to include {} ??"
    # 加上 {} 反而不行 ??? （可能用 {} 时，就要用if ()
    #awk '$0 ~ /Brown/' testdata/grade.txt  # 整行匹配, 默认动作是打印整行
    awk '/Brown/' testdata/grade.txt  # 默认整行匹配, 默认动作是打印整行

    # 精确匹配
    echo_blue "\nexact match"
    awk '$3==48 {print $0}' testdata/grade.txt  # 一个等号不行

    # 反向模糊匹配
    echo_red "\nreverse match"
    awk '$0 !~ /Brown/' testdata/grade.txt

    # 上面的匹配仍不够精确 ，可以针对 字段 来模糊匹配
    echo_red "\nfield reverse match"
    #awk '$4 !~ /Brown/ {print $0}' testdata/grade.txt  # 这样也行
    awk '{if($4 !~ /Brown/) print $0}' testdata/grade.txt

    # 精准匹配 （打印了所有行）
    echo_blue "\nfield reverse exact match"
    awk '$4 != "Brown" { print $0}' testdata/grade.txt

    echo_red "\nfield compare"
    awk '{if($6<$7) print $1}' testdata/grade.txt  # $1不能用双引号，否则打印出 $1 本身

    echo_red "capital match"
    awk '/[Gg]reen/' testdata/grade.txt

    echo_red "match any single character"
    awk '$1 ~ /^...a/' testdata/grade.txt  # 从行头开始，第4个字符是a

    echo_red "match OR relation"
    #awk '$4 ~ /Yellow|Brown/' testdata/grade.txt
    awk '$4 ~ /(Yellow|Brown)/' testdata/grade.txt # 貌似不加() 也可行

    echo_red "-------- compound logical expr"
    awk '{if ($1=="P.Bunny" && $4=="Yellow") print $0}' testdata/grade.txt
    # 同理有 || , !
}

t4_builtin_variable() {
    f=testdata/grade.txt
    echo_blue "count records number"
    awk 'END { print NR }' $f

    # NR表示从awk开始执行后，按照记录分隔符读取的数据次数，默认的记录分隔符为换行符，因此默认的就是读取的数据行数
    echo_blue "print NF,NR,$0"
    # FNR 在遇到新文件时会置位，而NR 不会
    awk '{print NF,NR,FNR, $0} END{print FILENAME "\n"}' $f \
        testdata/test.txt

    echo_red "given field input delimiter, field output delimiter"
    cat testdata/test.txt  | tr ' ' ',' | awk -F ',' -v OFS='#' '{print $1,$2}'

    echo_red "given record input delimiter, record output delimiter"
    # RS是记录分隔符，默认的分隔符是\n . 把记录看成以 '#' 分隔
    cat testdata/test.txt  | tr '\n' '#' | awk 'BEGIN{RS="#"; ORS="/"} {print $1,$2}'

    echo_blue "\nRS,RT match (even with RE)"
    echo "111 222|333 444|555 666" | awk 'BEGIN{RS="|"} {print $0,RT}'  # 最后一行没有RT
    # RS 也可以是正则(很有用！！)，RT是RS匹配出来的内容
    echo -n "111 222a333 444bcd555 666" | awk 'BEGIN{RS="[a-z]+"} {print $0,"\t",RS,RT}'  # -n 是防止最后一个记录把换行带上了

    echo_blue "\nget dir name"
    echo $PWD | awk -F / '{print $NF}'  # 打印最后一个字段，其实就是dir name

    f=testdata/grade.txt
    echo_red "\nprint each row sequence + file_name"
    # 统计所有.sh文件大小
    ls -l *.sh | awk '{sum+=$5} END {print "total *.sh size sum:",sum}'

    echo_blue "\nonly print match line and 2nd line below it"
    # 只取出匹配行 和 匹配行下的第2行
    # https://unix.stackexchange.com/questions/88038/print-matching-line-and-nth-line-from-the-matched-line
    # finds all lines that match pattern. When it finds a match it stores the record number (NR) in the array nr. 
    #   It also stores the 2th record from NR in the same array. This is done by the nr[NR+2]. Every record (NR) 
    #   is then checked to see if it's present in the nr array, if so the record is printed.
    awk '/Lulu/ {nr[NR]; nr[NR+2]}; NR in nr' $f
}

t5_operator() {
    f=testdata/grade.txt
    echo_red "-------- modify field value (in memory), printing all lines"
    # print是针对所有的行，不是仅仅是if 判断的行
    awk '{if ($1=="M.Tansley") $6=$6-1; print $1,$6,$7}' $f

    echo_red "modify field value, print modified line"
    # 加上{} 就只打印修改行了
    awk '{if ($1=="M.Tansley") {$6=$6-1; print $1,$6,$7} }' $f

    echo_red "\nadd new field according to calculation"
    # 给 $8取个变量名比如叫diff 也可以
    awk 'BEGIN{print "Name\t\tDifference"} {if ($6<$7) {$8=$7-$6; print $1 "\t" $8} }' $f | column -t

    echo_blue "\nusing variables for naming field"
    awk 'BEGIN { base=27} { if($6<base) print $0}' $f

    echo_red "\ncaluculate against column"
    # 给total+=加上 {}，就不打印所有行了
    awk '{total+=$6} END {print "total sixth column:" total}' $f

    echo_blue "\nprint all records including total sixth column"
    # 不加 {} 时，得在END前加上; ，否则语法错误。但此时会执行默认动作，打印所有的行
    awk 'total+=$6; END {print "total points:" total}' $f

    echo_red "\ncalculate all files total size"
    # 注意 pattern 后 接了2个 {}
    ls -l testdata | awk '/^[^d]/ {print $9"\t"$5} {total+=$5} END { print "total size:", total}'
    # `total+=$5` 没有用 {} 时，执行默认动作，打印所有的行。但前面的print 不会覆盖默认动作吗？
    #ls -l testdata | awk '/^[^d]/ {print $9"\t"$5} total+=$5; END { print "total size:", total}'
    # total不加 {} 不行
    #ls -l testdata | awk '/^[^d]/ {print $9"\t"$5} total+=$5 END { print "total size:", total}'
}

t6_builtin_function() {
    f=testdata/grade.txt
    echo_blue "--------------- using gsub"
    awk 'gsub(/4842/, 4899) {print $0}' $f  # 只打印匹配行

    echo_blue "\nusing length"
    awk '$1=="J.Troll" { print length($1),$4}' $f

    echo_blue "\nusing sub"
    # 这个竟然没输出 ???
    awk '$1=="J.Troll" sub(/26/, "29", $0)' $f  # sub 什么意思？？

    echo_blue "\nusing substr"
    # 位置从1开始
    awk '{print substr($1, 3)}' $f  # 包括 index 3，好像从1 开始计

    echo_blue "use split to create array"
    # 简单情况下，用 bash 的 () 可以很快创建一个 array
    awk 'BEGIN { record="123#456#789"; split(record, ar, "#") } END { for (i in ar) {print ar[i]} }' /dev/null  # /dev/null 这里给个fake input，否则会从stdin要
}

t7_printf_format() {
    f=testdata/grade.txt
    echo_red "---------- using printf"
    awk 'BEGIN {printf "%c\n", 65}'

    echo_red "\nname left-aligned"
    awk 'BEGIN { print "Name \t\tNumber"} {printf "%-15s %s\n", $1, $3}' $f
}

t8_pass_variables_to_awk() {
    f=testdata/grade.txt
    echo_blue "----------- pass variable to awk"
    awk 'age=10 {if ($5<age) print $0}' $f

    echo_blue "\nchecking free disk space"
    # 这与前面的 base=27的写法有区别吗？？这里的变量不是在BEGIN {} 里定义的
    df -m | grep -v 'Filesystem' | awk 'trigger=1600 { if($4<trigger) print $6"\t"$4 }'

    # 从一个命令中接受值，同时又从文件中读记录
    # https://stackoverflow.com/questions/59726081/pipe-command-output-into-awk
    echo_red "\npass command's output to awk"
    awk 'b="eeee" {print $0,a,b}' a="$(pwd)" b="ddddd" $f  # 关于变量 b，先读到的是外面的，再读里面的
    # awk  a="$(pwd)" '{print $0,a}' $f # 不能这样写
}

# https://stackoverflow.com/questions/39614454/creating-histograms-in-bash
# 很好的计算数据分布的脚本
t10_draw_hist() {
    echo_red "get histogram"
    grep 'took:' testdata/pg-insert-lat.log | awk '{print $NF}' | head -n 100 | ./awk/hist.awk
}

t11_select_text_between_patterns() {
    features=$(cat << eof
set(FEATURES
	DATALOG
	FASTPATH
	PERSISTENCE
)
option(DATALOG OFF)
OPTION(FASTPATH OFF)
eof
)

    aa=$(cat << eof
1
2
PAT1
3    - first block
4
PAT2
5
6
PAT1
7    - second block
PAT2
8
9
PAT1
10    - third block
eof
)
    # 一种朴素写法:选取pattern之间的内容
    # xargs 可用来把首尾空格都去掉
    echo "$features" | awk '/set\(FEATURES/,/\)/' | sed '1d' | sed '$d' | tr '\n' ',' | xargs

    # https://stackoverflow.com/questions/38972736/how-to-print-lines-between-two-patterns-inclusive-or-exclusive-in-sed-awk-or
    # This uses next to skip the line that contains PAT1 in order to avoid this being printed
    echo "$aa" | awk '/PAT1/{flag=1;next} /PAT2/{flag=0} flag' # PAT1,PAT2 pattern不能有逗号？
    # 链接里还有其他几种是否打印pattern行的写法
}


case $1 in
1)
    t1 ;;
2)
    t2_use_for_loop_to_construct_implicit_hashtable ;;
3)
    t3_reglar_expr ;;
4)
    t4_builtin_variable ;;
5)
    t5_operator ;;
6)
    t6_builtin_function ;;
7)
    t7_printf_format ;;
8)
    t8_pass_variables_to_awk ;;
9)
    echo_red "using .awk to process file "
    ./belts.awk testdata/grade_stu.txt
    ;;
10)
    t10_draw_hist ;;
11)
    t11_select_text_between_patterns ;;
*)
    echo_red "bad parameter" ;;
esac
